#include "cgzCHttpS.h"
#include "cgzCThreadPool.h"

cgzCHttpLoopS::cgzCHttpLoopS()
    : m_cmd(HTTPS_LOOP)
{
}

cgzCHttpLoopS::~cgzCHttpLoopS()
{
}

cgzCHttpListenS::cgzCHttpListenS(bool success)
    : m_success(success)
{
    m_cmd = EHTTP_S::HTTPS_LISTEN;
}

cgzCHttpListenS::~cgzCHttpListenS()
{
}

cgzCHttpCorsS::cgzCHttpCorsS()
{
    m_cmd = EHTTP_S::HTTPS_CORS;
    m_cors.clear();
}

cgzCHttpCorsS::~cgzCHttpCorsS()
{
    m_cors.clear();
}

cgzCHttpRecvS::cgzCHttpRecvS(http_request &request, std::string &uri, json::value &params)
    : m_request(std::move(request)), m_uri(uri), m_params(std::move(params))
{
}

cgzCHttpRecvS::~cgzCHttpRecvS()
{
}

cgzCHttpRGetS::cgzCHttpRGetS(http_request &request, std::string &uri, json::value &params)
    : cgzCHttpRecvS(request, uri, params)
{
    m_cmd = EHTTP_S::HTTPS_RGET;
}

cgzCHttpRGetS::~cgzCHttpRGetS()
{
}

cgzCHttpRPostS::cgzCHttpRPostS(http_request &request, std::string &uri, json::value &params)
    : cgzCHttpRecvS(request, uri, params)
{
    m_cmd = EHTTP_S::HTTPS_RPOST;
}

cgzCHttpRPostS::~cgzCHttpRPostS()
{
}

cgzCHttpRPutS::cgzCHttpRPutS(http_request &request, std::string &uri, json::value &params)
    : cgzCHttpRecvS(request, uri, params)
{
    m_cmd = EHTTP_S::HTTPS_RPUT;
}

cgzCHttpRPutS::~cgzCHttpRPutS()
{
}

cgzCHttpRDelS::cgzCHttpRDelS(http_request &request, std::string &uri, json::value &params)
    : cgzCHttpRecvS(request, uri, params)
{
    m_cmd = EHTTP_S::HTTPS_RDEL;
}

cgzCHttpRDelS::~cgzCHttpRDelS()
{
}

cgzCHttpRHeadS::cgzCHttpRHeadS(http_request &request, std::string &uri, json::value &params)
    : cgzCHttpRecvS(request, uri, params)
{
    m_cmd = EHTTP_S::HTTPS_RHEAD;
}

cgzCHttpRHeadS::~cgzCHttpRHeadS()
{
}

cgzCHttpROptsS::cgzCHttpROptsS(http_request &request, std::string &uri, json::value &params)
    : cgzCHttpRecvS(request, uri, params)
{
    m_cmd = EHTTP_S::HTTPS_ROPTS;
}

cgzCHttpROptsS::~cgzCHttpROptsS()
{
}

cgzCHttpS::cgzCHttpS(cgzCThreadPool *pool, const char *name, const char *url, int cid)
    : cgzITask(pool, name, cgzETask::CHttpS),
      m_pListener(nullptr),
      m_cid(cid)
{

    m_cors.clear();
    uri_builder uri(U(url));
    auto addr = uri.to_uri().to_string();

    http_listener_config config;
    config.set_ssl_context_callback([this](boost::asio::ssl::context &ctx)
                                    {
                                        try
                                        {
                                            ctx.set_options(boost::asio::ssl::context::default_workarounds);
                                            ctx.use_certificate_chain_file("./cert.pem");
                                            ctx.use_private_key_file("./private_key.pem", boost::asio::ssl::context::pem);
                                        }
                                        catch (const std::exception &e)
                                        {
                                            cgz_this_error("SSL configuration error:{}",e.what());
                                            ERR_print_errors_fp(stderr);
                                        } });

    m_pListener = new http_listener(addr, config);
    m_pListener->support(methods::GET, std::bind(&cgzCHttpS::get, this, std::placeholders::_1));
    m_pListener->support(methods::POST, std::bind(&cgzCHttpS::post, this, std::placeholders::_1));
    m_pListener->support(methods::PUT, std::bind(&cgzCHttpS::put, this, std::placeholders::_1));
    m_pListener->support(methods::DEL, std::bind(&cgzCHttpS::del, this, std::placeholders::_1));
    m_pListener->support(methods::HEAD, std::bind(&cgzCHttpS::head, this, std::placeholders::_1));
    m_pListener->support(methods::OPTIONS, std::bind(&cgzCHttpS::options, this, std::placeholders::_1));
    m_pListener->open().then([]()
                             { std::cout << "listen success" << std::endl; })
        .wait();

    // 关联线程池
    pool->addTask(m_name.c_str(), this);
    // 监听通知线程
    auto node = malloc(sizeof(cgzCHttpListenS));
    auto loop = new (node) cgzCHttpListenS(true);
    loop->target = m_cid;
    loop->origin = getId();
    todoret(loop);
}

void cgzCHttpS::get(http_request request)
{
    revice(request, EHTTP_S::HTTPS_RGET);
}

void cgzCHttpS::post(http_request request)
{

    revice(request, EHTTP_S::HTTPS_RPOST);
}

void cgzCHttpS::put(http_request request)
{
    revice(request, EHTTP_S::HTTPS_RPUT);
}

void cgzCHttpS::del(http_request request)
{
    revice(request, EHTTP_S::HTTPS_RDEL);
}

void cgzCHttpS::head(http_request request)
{
    revice(request, EHTTP_S::HTTPS_RHEAD);
}

void cgzCHttpS::options(http_request request)
{
    revice(request, EHTTP_S::HTTPS_ROPTS);
}

void cgzCHttpS::revice(http_request &request, EHTTP_S cmd)
{
    if (m_bShutdown)
    {
        return;
    }

    std::unique_lock lock(m_rwmutex);
    uri request_uri = request.request_uri();
    auto path = request_uri.path();
    auto params = json::value::object();
    decode(request, params);

    void *mem = nullptr;
    cgzCHttpRecvS *ret = nullptr;

    switch (cmd)
    {
    case EHTTP_S::HTTPS_RGET:
        mem = malloc(sizeof(cgzCHttpRGetS));
        ret = new (mem) cgzCHttpRGetS(request, path, params);
        break;
    case EHTTP_S::HTTPS_RPOST:
        mem = malloc(sizeof(cgzCHttpRPostS));
        ret = new (mem) cgzCHttpRPostS(request, path, params);
        break;
    case EHTTP_S::HTTPS_RPUT:
        mem = malloc(sizeof(cgzCHttpRPutS));
        ret = new (mem) cgzCHttpRPutS(request, path, params);
        break;
    case EHTTP_S::HTTPS_RDEL:
        mem = malloc(sizeof(cgzCHttpRDelS));
        ret = new (mem) cgzCHttpRDelS(request, path, params);
        break;
    case EHTTP_S::HTTPS_RHEAD:
        mem = malloc(sizeof(cgzCHttpRHeadS));
        ret = new (mem) cgzCHttpRHeadS(request, path, params);
        break;
    case EHTTP_S::HTTPS_ROPTS:
        mem = malloc(sizeof(cgzCHttpROptsS));
        ret = new (mem) cgzCHttpROptsS(request, path, params);
        break;
    default:
        break;
    }

    if (ret)
    {
        ret->target = m_cid;
        ret->origin = getId();
        todoret(ret);
    }
}

void cgzCHttpS::decode(http_request &request, web::json::value &params)
{
    uri request_uri = request.request_uri();
    // 解析查询参数并添加到 params
    auto query = request_uri.query();
    auto query_params = uri::split_query(query);
    for (const auto &param : query_params)
    {
        params[param.first] = json::value::string(web::uri::decode(param.second)); // 将参数添加到 JSON 对象
    }

    // json body 解析
    auto content_type = request.headers()["Content-Type"];
    if (content_type.find("application/x-msgpack") != std::string::npos)
    {
        auto body = request.extract_vector().get();
        auto obj = msgpack::unpack(reinterpret_cast<const char *>(body.data()), body.size());
        params["body"] = msgpack2json(obj.get(), this); // 将参数添加到 JSON 对象
    }
    else if (content_type.find("application/json") != std::string::npos)
    {
        params["body"] = request.extract_json().get(); // 将参数添加到 JSON 对象
    }
    else
    {
        auto body = request.extract_vector().get();
        params["body"] = json::value::string(std::string(reinterpret_cast<const char *>(body.data()), body.size())); // 将参数添加到 JSON 对象
    }
}

cgzCHttpS::~cgzCHttpS()
{
}

bool cgzCHttpS::todoret(cgzCHttpLoopS *cgzTask)
{
    m_synTask.push_back(cgzTask);
    setBusy();
    return true;
}

bool cgzCHttpS::casBusy()
{
    // 处理返回事件
    std::unique_lock lock(m_rwmutex);
    if (m_synTask.empty())
    {
        return false;
    }

    m_retTask.splice(m_retTask.end(), m_synTask);
    setFree();
    return true;
}

bool cgzCHttpS::run(int64_t nms)
{
    // 处理请求-返回结果
    while (!m_runTask.empty())
    {
        auto task = static_cast<cgzCHttpLoopS *>(m_runTask.front());
        m_runTask.pop_front();

        switch (task->m_cmd)
        {
        case EHTTP_S::HTTPS_LOOP:
        {
            retTask(task);
        }
        break;
        case EHTTP_S::HTTPS_CORS:
        {
            auto corsTask = static_cast<cgzCHttpCorsS *>(task);
            m_cors = corsTask->m_cors;
        }
        break;
        case EHTTP_S::HTTPS_RGET:
        case EHTTP_S::HTTPS_RPOST:
        case EHTTP_S::HTTPS_RPUT:
        case EHTTP_S::HTTPS_RDEL:
        case EHTTP_S::HTTPS_RHEAD:
        case EHTTP_S::HTTPS_ROPTS:
        {
            auto retTask = static_cast<cgzCHttpRecvS *>(task);
            // json::value js = json::value::parse(retTask->m_retstr.c_str());
            if (!m_cors.empty())
            {
                http_response response(status_codes::Created);
                for (auto &item : m_cors)
                {
                    response.headers().add(item.first, item.second);
                }
                response.set_body(retTask->m_retstr, utf8string("application/json; charset=utf-8"));
                retTask->m_request.reply(response);
            }
            else
            {
                http_response response(status_codes::MethodNotAllowed);
                response.set_body(retTask->m_retstr, utf8string("application/json; charset=utf-8"));
                retTask->m_request.reply(response);
            }
            static_cast<cgzCHttpLoopS *>(task)->~cgzCHttpLoopS();
            free(task);
        }
        break;
        default:
        {
            static_cast<cgzCHttpLoopS *>(task)->~cgzCHttpLoopS();
            free(task);
        }
        break;
        }
    }

    return true;
}

json::value msgpack2json(const msgpack::v1::object &obj, cgzITask *that)
{
    json::value js;

    switch (obj.type)
    {
    case msgpack::type::MAP:
    {
        js = json::value::object();
        for (size_t i = 0; i < obj.via.map.size; ++i)
        {
            const msgpack::v1::object_kv &kv = obj.via.map.ptr[i];
            std::string key = std::string(kv.key.via.str.ptr, kv.key.via.str.size);
            js[key] = msgpack2json(kv.val, that); // 递归调用
        }
        break;
    }
    case msgpack::type::ARRAY:
    {
        js = json::value::array();
        for (size_t i = 0; i < obj.via.array.size; ++i)
        {
            js[i] = msgpack2json(obj.via.array.ptr[i], that); // 递归调用
        }
        break;
    }
    case msgpack::type::STR:
    {
        js = json::value::string(std::string(obj.via.str.ptr, obj.via.str.size));
        break;
    }
    case msgpack::type::POSITIVE_INTEGER:
    {
        js = json::value::number(obj.via.u64);
        break;
    }
    case msgpack::type::NEGATIVE_INTEGER:
    {
        js = json::value::number(obj.via.i64);
        break;
    }
    case msgpack::type::FLOAT64:
    {
        js = json::value::number(obj.via.f64);
        break;
    }
    case msgpack::type::NIL:
    {
        js = json::value::null();
        break;
    }
    case msgpack::type::BOOLEAN:
    {
        js = json::value::boolean(obj.via.boolean);
        break;
    }
    default:
    {
        cgz_that_error("Unsupported type:{}", (int)obj.type);
    }
    }
    return js;
}
